Kapitel 6 Vererbung, Polymorphie und Schnittstellen
6.1 Basisklassen und abgeleitete Klassen  
Welche Fähigkeit würden Sie von einem Circle-Objekt neben den bereits implementierten noch erwarten? Wahrscheinlich eine ganz wesentliche, nämlich die Fähigkeit, sich in einer beliebigen grafikfähigen Komponente visualisieren zu können. Bisher fehlt dazu noch eine passende Methode.
Die Klasse Circle soll jedoch von uns als abgeschlossen betrachtet werden (zumindest hinsichtlich der Fähigkeiten eines Circle-Objekts). Damit simulieren wir zwei Ausgangssituationen, die in der täglichen Praxis häufig auftreten:
|
Die Implementierung einer Klasse, wie beispielsweise Circle, ist für viele Anwendungsfälle völlig ausreichend. Eine Erweiterung würde nicht allgemeinen, sondern nur speziellen Zusatzanforderungen genügen. |
|
Eine Klasse wird als Kompilat bereitgestellt. Damit besteht auch keine Möglichkeit, direkt die Klassenimplementierung um weitere Fähigkeiten zu ergänzen. Wie kann das Problem gelöst werden, eine Klasse um zusätzliche Fähigkeiten zu erweitern, damit sie weitergehenden Anforderungen gewachsen ist? |
Die Antwort ist sehr einfach und lautet: Es muss eine weitere Klasse entwickelt werden. Diese soll GraphicCircle heißen und einerseits alle Fähigkeiten der Klasse Circle veröffentlichen und darüber hinaus auch noch eine Methode namens Draw, damit das Objekt gezeichnet werden kann. Mit der Vererbung, einem der Stützpfeiler der objektorientierten Programmierung, ist die Lösung sehr einfach zu realisieren.
|
Das Grundprinzip der Vererbung ist, Methoden und Eigenschaften einer existierenden Klasse wiederzuverwenden und weitere hinzuzufügen.
|
Eine Klasse, die ihre Member als Erbgut einer abgeleiteten Klasse zur Verfügung stellt, wird als Basis- oder Superklasse bezeichnet. Die erbende Klasse ist die Sub- oder einfach nur die abgeleitete Klasse. Dem Grundprinzip der Vererbung folgend verfügen Subklassen normalerweise über mehr Funktionalitäten als ihre Basisklassen. Im Einzelfall muss überprüft werden, ob sich die Vererbung für die benutzerdefinierten Klassen einer Anwendung eignet.
Werden zwei Klassen, die miteinander in einer Vererbungsbeziehung stehen, grafisch dargestellt, wird – wie in Abbildung 6.1 – der Beziehungspfeil von der abgeleiteten in Richtung der beerbten Klasse gezeichnet.
Die Vererbung muss nicht mit dem Ableiten einer Klasse aus einer Basisklasse aufhören. Eine Subklasse kann ihrerseits selbst wieder als Basisklasse für weitere abgeleitete Klassen dienen. Aus einer Basisklasse können auch mehrere Subklassen abgeleitet werden, die dann allerdings untereinander beziehungslos sind. Am Ende kann dadurch eine nahezu beliebig tiefe und weit verzweigte Vererbungshierarchie entstehen, die einer Baumstruktur ähnelt. Die .NET-Klassenbibliothek mit der an der Spitze stehenden Basisklasse Object ist das für uns aktuelle Paradebeispiel einer solchen Klassenhierarchie.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 6.1 Die Vererbungsbeziehung zwischen den Klassen »Circle« und »GraphicCircle«
Grundsätzlich wird in der Objektorientierung zwischen einfacher Vererbung und Mehrfachvererbung unterschieden. Bei der einfachen Vererbung kann eine Klasse nur aus einer direkten Basisklasse abgeleitet werden, bei der Mehrfachvererbung können es mehrere sein. Eine Klassenhierarchie, die auf Mehrfachvererbung basiert, ist schwierig zu entwickeln und kann im schlimmsten Fall zu unerwarteten Nebeneffekten führen. Um Konflikten dieser Art aus dem Weg zu gehen, wird die Mehrfachvererbung von .NET nicht unterstützt. Damit werden bewusst Einschränkungen in Kauf genommen, die aber durch die Schnittstellentechnologie nahezu gleichwertig ersetzt werden (siehe Abschnitt 6.10).
6.1.1 Die Ableitung einer Klasse  
Wenden wir uns nun wieder unserem Beispiel zu und ergänzen das Projekt CircleApplication um die Klasse GraphicCircle, welche die Basisklasse Circle beerben soll. Sie können GraphicCircle in einer neuen Quellcodedatei implementieren oder in der, die schon die Klasse Circle enthält. Die bessere Übersicht bei der Codeverwaltung spricht vielleicht eher für eine eigene Datei.
GraphicCircle soll aus Circle abgeleitet und durch die typspezifische Methode Draw erweitert werden. Die Ableitung wird in der neuen Klassendefinition durch die Anweisung Inherits mit Angabe der Basisklasse zum Ausdruck gebracht:
| Public Class GraphicCircle
|
| Inherits Circle
|
| Public Sub Draw()
|
| Console.WriteLine("Der kreis wird gezeichnet.")
|
| End Sub
|
| End Class
|
Wir wollen an dieser Stelle das Kreisobjekt nicht wirklich zeichnen, sondern nur stellvertretend eine Zeichenfolge an der Konsole ausgeben.
Die Konsequenz der Vererbung können Sie zu diesem Zeitpunkt bereits sehen, wenn Sie ein Objekt des Typs GraphicCircle mit
| Dim gc As GraphicCircle = New GraphicCircle()
|
erzeugen und anschließend die Punktnotation auf den Objektverweis anwenden: Es werden in der Intellisense-Liste neben der neuen Methode Draw alle öffentlichen Mitglieder der Klasse Circle angezeigt, obwohl diese in der abgeleiteten Klasse nicht definiert sind (siehe Abbildung 6.2).
 Hier klicken, um das Bild zu Vergrößern
Abbildung 6.2 Die von der Klasse »Circle« geerbten Fähigkeiten
Die Tatsache, dass ein Objekt vom Typ GraphicCircle alle Komponenten der Klasse Circle offen legt, lässt unweigerlich den Schluss zu, dass das Objekt einer abgeleiteten Klasse gleichzeitig auch ein Objekt der Basisklasse sein muss. Zwischen den beiden in der Vererbungshierarchie in Beziehung stehenden Klassen existiert eine Beziehung, die als Ist-ein(e)-Beziehung bezeichnet wird.
|
Ein Objekt vom Typ einer abgeleiteten Klasse ist gleichzeitig auch immer ein Objekt vom Typ seiner Basisklasse.
|
Betrachten Sie zur Verdeutlichung noch einmal Abbildung 4.2. Aus der Basisklasse Luftfahrzeug werden die Klassen Hubschrauber, Starrflügler und Zeppelin abgeleitet. Sie können sicher der Behauptung zustimmen, dass ein Zeppelin ein Luftfahrzeug ist. Der Behauptung, dass ein Luftfahrzeug grundsätzlich ein Hubschrauber ist, müssten Sie aber widersprechen, denn es könnte sich auch um einen Starrflügler oder um einen Zeppelin handeln.
6.1.2 Klassen, die nicht vererben können  
Klassen, die abgeleitet werden, vererben ihren Subklassen alle Eigenschaften und Methoden. Es kommt aber immer wieder vor, dass die weitere Ableitung einer Klasse keinen Sinn macht oder sogar vermieden werden muss, weil die von der Klasse zur Verfügung gestellten Dienste als endgültig betrachtet werden können. Typischerweise gehören hierzu Klassen, die nur statische Methoden veröffentlichen. Stellvertretend für viele andere sei an dieser Stelle die Klasse Math der .NET-Klassenbibliothek genannt.
Um sicherzustellen, dass eine Klasse nicht weiter abgeleitet werden kann, wird in der Klassendefinition der Modifizierer NotInheritable angegeben:
| Public NotInheritable Class ClassA
|
| ' Anweisungen
|
| End Class
|
Nicht ableitbare Klassen werden auch als versiegelte Klassen bezeichnet. Der Versuch, die Klasse ClassA abzuleiten, wird zu einer Fehlermeldung führen. Strukturen, die per Definition nicht beerbt werden können, sind implizit versiegelt.
6.1.3 Zusammenfassung  
|
Eine Klasse, die ihr Verhalten anderen Klassen vererbt, wird als Basisklasse bezeichnet, die erbenden Klassen sind die Sub- oder abgeleiteten Klassen. Zwischen einer Basisklasse und der daraus abgeleiteten Klasse besteht eine Ist-ein-Beziehung. |
|
.NET unterstützt nur die Einfachvererbung, nach der eine Klasse nur eine direkte Basisklasse haben kann. |
|
Abgeleitet wird eine Klasse, indem hinter dem Klassenbezeichner in einer neuen Anweisungszeile das Schlüsselwort Inherits gefolgt vom Bezeichner der Basisklasse angegeben wird. |
|
Grundsätzlich sind alle Klassendefinitionen ableitbar. Eine Ausnahme bilden die Klassen, die mit NotInheritable als nicht ableitbar definiert sind. Eine nicht ableitbare Klasse kann niemals eine Basisklasse sein. |
|